#include "ImageLabel.h"
#include <QPainter>         // 用于绘制图形
#include <QMenu>            // 用于上下文菜单
#include <QDialog>          // 用于弹出对话框查看大图
#include <QVBoxLayout>      // 用于布局大图对话框
#include <QLabel>           // 用于显示图像
#include <cmath>            // 用于几何计算（如 hypot）
#include <QMouseEvent>
#include <QDebug>
// 构造函数，启用鼠标跟踪功能
ImageLabel::ImageLabel(QWidget *parent) : QLabel(parent) {
    setMouseTracking(true);  // 即使不按下鼠标也能接收到 mouseMoveEvent
}
// 获取当前绘制的点集合（多边形模式）
const std::vector<QPoint>& ImageLabel::getPoints() const {
    return points;
}
// 清空所有绘制内容，包括点、矩形、闭环标记等
void ImageLabel::clearAll() {
    points.clear();
    closed = false;
    isRectMode = false;
    rectStartPoint = rectEndPoint = QPoint();
    mappedRectStart = mappedRectEnd = QPoint(); // 添加此行
    showMappedRect = false; // 添加此行
    update();  // 触发重绘
}


// 【新增】通用的 setOriginalPixmap 函数，并移除 setOriginalPixmapD1 和 setOriginalPixmapD2
void ImageLabel::setOriginalPixmap(const QPixmap &pixmap) {
    originalPixmap = pixmap;
    if (!originalPixmap.isNull()) {
        scaledPixmap =originalPixmap.scaled(size(),
                                             Qt::KeepAspectRatio, Qt::SmoothTransformation);
        setPixmap(scaledPixmap);
    } else {
        scaledPixmap = QPixmap();
        clear(); // 清除显示的图像
    }
    update();
}
// 添加新函数-此段的代码是根据映射点的左边再图2上进行绘制,此处的代码是对坐标点的位置进行调整
void ImageLabel::setMappedRect(const QPoint &start, const QPoint &end)
{
    mappedRectStart = start;
    mappedRectEnd = end;
    showMappedRect = true;
    update(); // 触发重绘以显示矩形
}
// 旋转图像并更新显示
void ImageLabel::rotateImage(int angle) {
    if (originalPixmap.isNull()) return;
    QTransform transform;
    transform.rotate(angle);
    originalPixmap = originalPixmap.transformed(transform, Qt::SmoothTransformation);
    setOriginalPixmap(originalPixmap); // 使用新函数更新状态
}

// 将控件（QLabel）中的坐标映射为图像（originalPixmap1）中的实际像素坐标
// 【修改】mapToImage 和 mapToWidget 使用通用成员
QPoint ImageLabel::mapToImage(const QPoint& widgetPos) const {
    if (originalPixmap.isNull()) return {};
    QSize labelSize = size();
    QSize imgSize = scaledPixmap.size();
    int offsetX = (labelSize.width() - imgSize.width()) / 2;
    int offsetY = (labelSize.height() - imgSize.height()) / 2;
    QPoint imgPos = widgetPos - QPoint(offsetX, offsetY);
    double scaleX = double(originalPixmap.width()) / imgSize.width();
    double scaleY = double(originalPixmap.height()) / imgSize.height();
    return QPoint(int(imgPos.x() * scaleX), int(imgPos.y() * scaleY));
}

// 将图像坐标映射为控件坐标（用于绘图显示）
QPoint ImageLabel::mapToWidget(const QPoint& imgPt) const {
    if (originalPixmap.isNull()) return {};
    QSize labelSize = size();
    QSize imgSize = scaledPixmap.size();
    int offsetX = (labelSize.width() - imgSize.width()) / 2;
    int offsetY = (labelSize.height() - imgSize.height()) / 2;
    double scaleX = double(imgSize.width()) / originalPixmap.width();
    double scaleY = double(imgSize.height()) / originalPixmap.height();
    return QPoint(int(imgPt.x() * scaleX) + offsetX, int(imgPt.y() * scaleY) + offsetY);
}

// 鼠标按下事件，处理多边形标注或矩形绘制的逻辑
// 【修改】所有事件处理函数中的逻辑
void ImageLabel::mousePressEvent(QMouseEvent *event) {
    // 如果图像为空，或者按下的不是鼠标左键，则忽略该事件
    if (originalPixmap.isNull() || event->button() != Qt::LeftButton)
        return;
    // 将鼠标在控件中的位置映射为原始图像坐标（考虑缩放与居中）
    QPoint imgPt = mapToImage(event->pos());
    // 判断当前是否处于“矩形框模式”
    if (isRectMode) {
        // qDebug() << "[调试] mousePressEvent: 矩形模式，起点/终点初始化为" << imgPt;
        rectStartPoint = imgPt;    // 记录矩形的起始点，同时初始化终止点为相同位置
        isDrawingRect = true;     // 设置标志位，表示开始绘制矩形
    } else {
        // 如果当前处于多边形模式，且已经闭合多边形，则不允许再添加点
        if (closed) return;
        // 如果已有三个或更多点，检查当前点击点是否靠近第一个点
        if (points.size() >= 3) {
            QPoint first = points[0];
            // 计算当前点击点与第一个点之间的欧几里得距离
            int dist = std::hypot(imgPt.x() - first.x(), imgPt.y() - first.y());
            // 如果距离小于10像素，则判定为“闭合多边形”
            if (dist < 10) {
                closed = true;     // 设置闭环标志
                update();          // 触发重绘（用于画出首尾连接线）
                return;            // 返回，不再添加当前点
            }
        }
        // 否则，当前点击的点为新的一部分，加入到点集当中
        points.push_back(imgPt);
    }
    // 最后统一更新图像控件，触发 paintEvent() 重绘
    update();
}
// 鼠标移动事件，实时更新矩形框终点
void ImageLabel::mouseMoveEvent(QMouseEvent *event) {
    if (isRectMode && isDrawingRect) {
        rectEndPoint = mapToImage(event->pos());
        //qDebug() << "[调试] mouseMoveEvent: 拖拽中，终点更新为" << rectEndPoint;
        update();
    }
}
// 鼠标释放事件处理函数，用于完成矩形框绘制
void ImageLabel::mouseReleaseEvent(QMouseEvent *event) {
    // 如果当前是矩形模式（isRectMode == true），并且正在绘制矩形（isDrawingRect == true）
    // 并且此次鼠标释放的是左键
    if (isRectMode && isDrawingRect && event->button() == Qt::LeftButton) {
        // 将鼠标释放的位置从窗口坐标转换为图像上的坐标，作为矩形的终点
        rectEndPoint = mapToImage(event->pos());

        qDebug() << "[调试] mouseReleaseEvent: 拖拽结束，最终起点:" << rectStartPoint << "终点:" << rectEndPoint;
        update();        // 调用 update()，触发界面重绘，以显示最终绘制好的矩形

        // 将坐标转化成x,y,w,h的样式
        double Rectangle_x = rectStartPoint.x();
        double Rectangle_y = rectStartPoint.y();
        double Rectangle_w = rectEndPoint.x() - rectStartPoint.x();
        double Rectangle_h = rectEndPoint.y() - rectStartPoint.y();

        // qDebug() << "Rectangle: " << Rectangle_x << " " << Rectangle_y << " " << Rectangle_w << " " << Rectangle_h<< endl;
        // 实现坐标点的归一化处理
        double N_Rectangle_x = Rectangle_x / originalPixmap.width();
        double N_Rectangle_y = Rectangle_y / originalPixmap.height();
        double N_Rectangle_w = Rectangle_w / originalPixmap.width();
        double N_Rectangle_h = Rectangle_h / originalPixmap.height();

        // qDebug() << "Rectangle: " << N_Rectangle_x << " " << N_Rectangle_y << " " << N_Rectangle_w << " " << N_Rectangle_h<< endl;
        isDrawingRect = false;        // 设置标志，表示不再处于绘制状态
    }
    emit pointsUpdated();// 在释放的时候再放开
}

// 重绘事件，绘制点、线、多边形或矩形框
// 重绘事件：当控件需要刷新界面时调用（自动或手动调用 update() 时触发）
void ImageLabel::paintEvent(QPaintEvent *event) {
    QLabel::paintEvent(event);  // 保留 QLabel 的绘图逻辑
    if (originalPixmap.isNull()) return;   // 调用 QLabel 的原始绘制函数，确保图片或文本等 QLabel 默认内容正常显示。如果没有加载原始图像，就不绘制任何标注内容，直接退出。
    QPainter painter(this); // 创建一个 QPainter，准备在当前控件（this，即 ImageLabel）上绘图。
    painter.setRenderHint(QPainter::Antialiasing); // 设置抗锯齿，让线条和平滑图形边缘更圆润、不锯齿化。
    // 绘制多边形点
    painter.setPen(QPen(Qt::red, 2)); //  设置画笔颜色为红色，线宽为 2，画刷颜色也为红色（用于填充点）
    //painter.setBrush(Qt::red);
    painter.setBrush(Qt::NoBrush);   // ⭐ 取消填充

    for (size_t i = 0; i < points.size(); ++i) {
        QPoint pt = mapToWidget(points[i]);
        painter.drawEllipse(pt, 2, 2);  // 绘制小圆点
        if (i > 0) { // 遍历用户点击添加的点（在原图上的点），将其映射到 QLabel 上的位置，并绘制小圆点表示。
            QPoint prev = mapToWidget(points[i - 1]);
            painter.drawLine(prev, pt);  // 连线
        }
    }// 如果当前点不是第一个点，就和前一个点连线，实现逐步构建多边形的边。

    // 若已闭环，连接首尾 若闭环，则首尾连线
    if (closed && points.size() >= 3) {
        QPoint first = mapToWidget(points[0]);
        QPoint last = mapToWidget(points.back());
        painter.drawLine(last, first);
    }//  如果多边形已闭环（用户点击第一个点闭合），则将最后一个点与第一个点连线，封闭这个区域。

    // 绘制矩形框 绘制矩形标注框（在矩形模式下）
    if (isRectMode && !rectStartPoint.isNull() && !rectEndPoint.isNull()) {// 判断当前是否在矩形框模式，且起点、终点都有效。
        // // 图像坐标构建矩形
        QRect imgRect(rectStartPoint, rectEndPoint);// 用图像坐标构建一个矩形（原始图像中的框区域）
        // 将图像中的矩形坐标映射到 QLabel 上，用于显示。
        QRect displayRect(mapToWidget(imgRect.topLeft()), mapToWidget(imgRect.bottomRight()));
        painter.setPen(QPen(Qt::blue, 2));
        painter.drawRect(displayRect);
    }

    // 【新增】绘制映射到图2的矩形框
    if (showMappedRect && !mappedRectStart.isNull() && !mappedRectEnd.isNull()) {
        painter.setPen(QPen(Qt::green, 2, Qt::DashLine)); // 使用绿色虚线以作区分
        painter.drawRect(QRect(mapToWidget(mappedRectStart), mapToWidget(mappedRectEnd)));
    }

    painter.setRenderHint(QPainter::Antialiasing);
    // **绘制映射的多边形/点（绿色，半透明填充）**
    if (showMappedPolygon && !mappedPolygon.empty()) {
        // 先设置画笔和画刷颜色
        painter.setPen(QPen(Qt::green, 2));
        painter.setBrush(QColor(0, 255, 0, 100));  // 绿色半透明填充

        QPolygon polygon(mappedPolygon.size());
        for (int i = 0; i < (int)mappedPolygon.size(); i++) {
            // 注意：mappedPolygon里面的点是图像坐标，需要映射到控件坐标
            QPoint ptWidget = mapToWidget(mappedPolygon[i]);
            polygon.setPoint(i, ptWidget);
            // 画点
            painter.drawEllipse(ptWidget, 4, 4);
        }
        painter.drawPolygon(polygon);
    }
}
// 鼠标双击事件：弹出窗口查看图像大图
void ImageLabel::mouseDoubleClickEvent(QMouseEvent *event) {
    if (originalPixmap.isNull()) return;
    QDialog *dialog = new QDialog(this);
    QVBoxLayout *layout = new QVBoxLayout(dialog);
    QLabel *label = new QLabel(dialog);

    // 缩放图像用于大图显示
    label->setPixmap(originalPixmap.scaled(1000, 1000, Qt::KeepAspectRatio));
    label->setAlignment(Qt::AlignCenter);
    layout->addWidget(label);

    dialog->setWindowTitle("查看大图");
    dialog->resize(1024, 768);
    dialog->exec();  // 模态显示
}
// 右键菜单事件，提供功能切换与操作入口
void ImageLabel::contextMenuEvent(QContextMenuEvent *event) {
    QMenu menu(this);
    QAction *toggleMode = menu.addAction(isRectMode ? "切换到分割模式" : "切换到矩形框模式");
    QAction *rotateLeft = menu.addAction("左旋转90°");
    QAction *rotateRight = menu.addAction("右旋转90°");
    QAction *clearAllAct = menu.addAction("清除所有标注");
    // 切换模式：从矩形切换到多边形，或反之
    connect(toggleMode, &QAction::triggered, this, [=]() {
        isRectMode = !isRectMode;
        points.clear();
        closed = false;
        rectStartPoint = rectEndPoint = QPoint();
        update();
    });

    // 左旋转图像
    connect(rotateLeft, &QAction::triggered, this, [=]() {
        rotateImage(-90);
    });

    // 右旋转图像
    connect(rotateRight, &QAction::triggered, this, [=]() {
        rotateImage(90);
    });

    // 清除所有标注
    connect(clearAllAct, &QAction::triggered, this, [=]() {
        clearAll();
    });

    // 弹出菜单
    menu.exec(event->globalPos());
}

// 获得矩形框的起点和终点
QPoint ImageLabel::getRectStartPoint() const {
    return rectStartPoint;
}
// 获得矩形框的终点
QPoint ImageLabel::getRectEndPoint() const {
    return rectEndPoint;
}


// 获得当前的绘制状态
bool ImageLabel::getDranwingReaction()
{
    return isDrawingRect;
}